diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/test/core/Application.js b/test/core/Application.js
index aa726b0..00eb286 100644
--- a/test/core/Application.js
+++ b/test/core/Application.js
@@ -17,4 +17,21 @@
done();
});
});
+
+ it('should remove canvas when destroyed', function (done)
+ {
+ const app = new PIXI.Application();
+ const view = app.view;
+
+ expect(view).to.be.instanceof(HTMLCanvasElement);
+ document.body.appendChild(view);
+
+ app.ticker.addOnce(() =>
+ {
+ expect(document.body.contains(view)).to.be.true;
+ app.destroy(true);
+ expect(document.body.contains(view)).to.be.false;
+ done();
+ });
+ });
});
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/test/core/Application.js b/test/core/Application.js
index aa726b0..00eb286 100644
--- a/test/core/Application.js
+++ b/test/core/Application.js
@@ -17,4 +17,21 @@
done();
});
});
+
+ it('should remove canvas when destroyed', function (done)
+ {
+ const app = new PIXI.Application();
+ const view = app.view;
+
+ expect(view).to.be.instanceof(HTMLCanvasElement);
+ document.body.appendChild(view);
+
+ app.ticker.addOnce(() =>
+ {
+ expect(document.body.contains(view)).to.be.true;
+ app.destroy(true);
+ expect(document.body.contains(view)).to.be.false;
+ done();
+ });
+ });
});
diff --git a/test/core/Graphics.js b/test/core/Graphics.js
index 11e47a0..505df4c 100644
--- a/test/core/Graphics.js
+++ b/test/core/Graphics.js
@@ -140,6 +140,28 @@
expect(graphics.containsPoint(point)).to.be.false;
});
+
+ it('should return false with hole', function ()
+ {
+ const point1 = new PIXI.Point(1, 1);
+ const point2 = new PIXI.Point(5, 5);
+ const graphics = new PIXI.Graphics();
+
+ graphics.beginFill(0)
+ .moveTo(0, 0)
+ .lineTo(10, 0)
+ .lineTo(10, 10)
+ .lineTo(0, 10)
+ // draw hole
+ .moveTo(2, 2)
+ .lineTo(8, 2)
+ .lineTo(8, 8)
+ .lineTo(2, 8)
+ .addHole();
+
+ expect(graphics.containsPoint(point1)).to.be.true;
+ expect(graphics.containsPoint(point2)).to.be.false;
+ });
});
describe('arc', function ()
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/test/core/Application.js b/test/core/Application.js
index aa726b0..00eb286 100644
--- a/test/core/Application.js
+++ b/test/core/Application.js
@@ -17,4 +17,21 @@
done();
});
});
+
+ it('should remove canvas when destroyed', function (done)
+ {
+ const app = new PIXI.Application();
+ const view = app.view;
+
+ expect(view).to.be.instanceof(HTMLCanvasElement);
+ document.body.appendChild(view);
+
+ app.ticker.addOnce(() =>
+ {
+ expect(document.body.contains(view)).to.be.true;
+ app.destroy(true);
+ expect(document.body.contains(view)).to.be.false;
+ done();
+ });
+ });
});
diff --git a/test/core/Graphics.js b/test/core/Graphics.js
index 11e47a0..505df4c 100644
--- a/test/core/Graphics.js
+++ b/test/core/Graphics.js
@@ -140,6 +140,28 @@
expect(graphics.containsPoint(point)).to.be.false;
});
+
+ it('should return false with hole', function ()
+ {
+ const point1 = new PIXI.Point(1, 1);
+ const point2 = new PIXI.Point(5, 5);
+ const graphics = new PIXI.Graphics();
+
+ graphics.beginFill(0)
+ .moveTo(0, 0)
+ .lineTo(10, 0)
+ .lineTo(10, 10)
+ .lineTo(0, 10)
+ // draw hole
+ .moveTo(2, 2)
+ .lineTo(8, 2)
+ .lineTo(8, 8)
+ .lineTo(2, 8)
+ .addHole();
+
+ expect(graphics.containsPoint(point1)).to.be.true;
+ expect(graphics.containsPoint(point2)).to.be.false;
+ });
});
describe('arc', function ()
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 6e1109c..0a7919d 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -70,26 +70,6 @@
expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
});
- it('should be added to the texture cache correctly using legacy addTextureToCache, '
- + 'and should remove also remove the base texture from its cache with removeTextureFromCache', function ()
- {
- cleanCache();
-
- const texture = new PIXI.Texture(new PIXI.BaseTexture());
-
- PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
- PIXI.Texture.addTextureToCache(texture, NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
- PIXI.Texture.removeTextureFromCache(NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
- });
-
it('should remove Texture from entire cache using removeFromCache (by Texture instance)', function ()
{
cleanCache();
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/test/core/Application.js b/test/core/Application.js
index aa726b0..00eb286 100644
--- a/test/core/Application.js
+++ b/test/core/Application.js
@@ -17,4 +17,21 @@
done();
});
});
+
+ it('should remove canvas when destroyed', function (done)
+ {
+ const app = new PIXI.Application();
+ const view = app.view;
+
+ expect(view).to.be.instanceof(HTMLCanvasElement);
+ document.body.appendChild(view);
+
+ app.ticker.addOnce(() =>
+ {
+ expect(document.body.contains(view)).to.be.true;
+ app.destroy(true);
+ expect(document.body.contains(view)).to.be.false;
+ done();
+ });
+ });
});
diff --git a/test/core/Graphics.js b/test/core/Graphics.js
index 11e47a0..505df4c 100644
--- a/test/core/Graphics.js
+++ b/test/core/Graphics.js
@@ -140,6 +140,28 @@
expect(graphics.containsPoint(point)).to.be.false;
});
+
+ it('should return false with hole', function ()
+ {
+ const point1 = new PIXI.Point(1, 1);
+ const point2 = new PIXI.Point(5, 5);
+ const graphics = new PIXI.Graphics();
+
+ graphics.beginFill(0)
+ .moveTo(0, 0)
+ .lineTo(10, 0)
+ .lineTo(10, 10)
+ .lineTo(0, 10)
+ // draw hole
+ .moveTo(2, 2)
+ .lineTo(8, 2)
+ .lineTo(8, 8)
+ .lineTo(2, 8)
+ .addHole();
+
+ expect(graphics.containsPoint(point1)).to.be.true;
+ expect(graphics.containsPoint(point2)).to.be.false;
+ });
});
describe('arc', function ()
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 6e1109c..0a7919d 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -70,26 +70,6 @@
expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
});
- it('should be added to the texture cache correctly using legacy addTextureToCache, '
- + 'and should remove also remove the base texture from its cache with removeTextureFromCache', function ()
- {
- cleanCache();
-
- const texture = new PIXI.Texture(new PIXI.BaseTexture());
-
- PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
- PIXI.Texture.addTextureToCache(texture, NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
- PIXI.Texture.removeTextureFromCache(NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
- });
-
it('should remove Texture from entire cache using removeFromCache (by Texture instance)', function ()
{
cleanCache();
diff --git a/test/core/util.js b/test/core/util.js
index 239aaeb..68c7e6d 100755
--- a/test/core/util.js
+++ b/test/core/util.js
@@ -280,39 +280,9 @@
describe('.removeItems', function ()
{
- var arr;
-
- beforeEach(() =>
+ it('should exist', function ()
{
- arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
- });
-
- it('should return if the start index is greater than or equal to the length of the array', function ()
- {
- PIXI.utils.removeItems(arr, arr.length + 1, 5);
- expect(arr.length).to.be.equal(10);
- });
-
- it('should return if the remove count is 0', function ()
- {
- PIXI.utils.removeItems(arr, 2, 0);
- expect(arr.length).to.be.equal(10);
- });
-
- it('should remove the number of elements specified from the array, starting from the start index', function ()
- {
- const res = [1, 2, 3, 8, 9, 10];
-
- PIXI.utils.removeItems(arr, 3, 4);
- expect(arr).to.be.deep.equal(res);
- });
-
- it('should remove other elements if delete count is > than the number of elements after start index', function ()
- {
- const res = [1, 2, 3, 4, 5, 6, 7];
-
- PIXI.utils.removeItems(arr, 7, 10);
- expect(arr).to.be.deep.equal(res);
+ expect(PIXI.utils.removeItems).to.be.a('function');
});
});
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/test/core/Application.js b/test/core/Application.js
index aa726b0..00eb286 100644
--- a/test/core/Application.js
+++ b/test/core/Application.js
@@ -17,4 +17,21 @@
done();
});
});
+
+ it('should remove canvas when destroyed', function (done)
+ {
+ const app = new PIXI.Application();
+ const view = app.view;
+
+ expect(view).to.be.instanceof(HTMLCanvasElement);
+ document.body.appendChild(view);
+
+ app.ticker.addOnce(() =>
+ {
+ expect(document.body.contains(view)).to.be.true;
+ app.destroy(true);
+ expect(document.body.contains(view)).to.be.false;
+ done();
+ });
+ });
});
diff --git a/test/core/Graphics.js b/test/core/Graphics.js
index 11e47a0..505df4c 100644
--- a/test/core/Graphics.js
+++ b/test/core/Graphics.js
@@ -140,6 +140,28 @@
expect(graphics.containsPoint(point)).to.be.false;
});
+
+ it('should return false with hole', function ()
+ {
+ const point1 = new PIXI.Point(1, 1);
+ const point2 = new PIXI.Point(5, 5);
+ const graphics = new PIXI.Graphics();
+
+ graphics.beginFill(0)
+ .moveTo(0, 0)
+ .lineTo(10, 0)
+ .lineTo(10, 10)
+ .lineTo(0, 10)
+ // draw hole
+ .moveTo(2, 2)
+ .lineTo(8, 2)
+ .lineTo(8, 8)
+ .lineTo(2, 8)
+ .addHole();
+
+ expect(graphics.containsPoint(point1)).to.be.true;
+ expect(graphics.containsPoint(point2)).to.be.false;
+ });
});
describe('arc', function ()
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 6e1109c..0a7919d 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -70,26 +70,6 @@
expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
});
- it('should be added to the texture cache correctly using legacy addTextureToCache, '
- + 'and should remove also remove the base texture from its cache with removeTextureFromCache', function ()
- {
- cleanCache();
-
- const texture = new PIXI.Texture(new PIXI.BaseTexture());
-
- PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
- PIXI.Texture.addTextureToCache(texture, NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
- PIXI.Texture.removeTextureFromCache(NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
- });
-
it('should remove Texture from entire cache using removeFromCache (by Texture instance)', function ()
{
cleanCache();
diff --git a/test/core/util.js b/test/core/util.js
index 239aaeb..68c7e6d 100755
--- a/test/core/util.js
+++ b/test/core/util.js
@@ -280,39 +280,9 @@
describe('.removeItems', function ()
{
- var arr;
-
- beforeEach(() =>
+ it('should exist', function ()
{
- arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
- });
-
- it('should return if the start index is greater than or equal to the length of the array', function ()
- {
- PIXI.utils.removeItems(arr, arr.length + 1, 5);
- expect(arr.length).to.be.equal(10);
- });
-
- it('should return if the remove count is 0', function ()
- {
- PIXI.utils.removeItems(arr, 2, 0);
- expect(arr.length).to.be.equal(10);
- });
-
- it('should remove the number of elements specified from the array, starting from the start index', function ()
- {
- const res = [1, 2, 3, 8, 9, 10];
-
- PIXI.utils.removeItems(arr, 3, 4);
- expect(arr).to.be.deep.equal(res);
- });
-
- it('should remove other elements if delete count is > than the number of elements after start index', function ()
- {
- const res = [1, 2, 3, 4, 5, 6, 7];
-
- PIXI.utils.removeItems(arr, 7, 10);
- expect(arr).to.be.deep.equal(res);
+ expect(PIXI.utils.removeItems).to.be.a('function');
});
});
diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js
index de81597..3f84d6a 100644
--- a/test/interaction/InteractionManager.js
+++ b/test/interaction/InteractionManager.js
@@ -128,6 +128,95 @@
});
});
+ describe('touch vs pointer', function ()
+ {
+ it('should call touchstart and pointerdown when touch event and pointer supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10);
+
+ expect(touchSpy).to.have.been.calledOnce;
+ expect(pointerSpy).to.have.been.calledOnce;
+ });
+
+ it('should not call touchstart or pointerdown when pointer event and touch supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10, 0, true);
+
+ expect(touchSpy).to.not.have.been.called;
+ expect(pointerSpy).to.not.have.been.called;
+ });
+
+ it('should call touchstart and pointerdown when touch event and pointer not supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, false);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10);
+
+ expect(touchSpy).to.have.been.calledOnce;
+ expect(pointerSpy).to.have.been.calledOnce;
+ });
+
+ it('should call touchstart and pointerdown when pointer event and touch not supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ pointer.interaction.supportsTouchEvents = false;
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10, 0, true);
+
+ expect(touchSpy).to.have.been.calledOnce;
+ expect(pointerSpy).to.have.been.calledOnce;
+ });
+ });
+
describe('add/remove events', function ()
{
let stub;
@@ -283,7 +372,7 @@
expect(element.removeEventListener).to.have.been.calledWith('mouseover');
});
- it('should add and remove touch events to element', function ()
+ it('should add and remove touch events to element without pointer events', function ()
{
const manager = new PIXI.interaction.InteractionManager(sinon.stub());
const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() };
@@ -306,6 +395,30 @@
expect(element.removeEventListener).to.have.been.calledWith('touchend');
expect(element.removeEventListener).to.have.been.calledWith('touchmove');
});
+
+ it('should add and remove touch events to element with pointer events', function ()
+ {
+ const manager = new PIXI.interaction.InteractionManager(sinon.stub());
+ const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() };
+
+ manager.interactionDOMElement = element;
+ manager.supportsPointerEvents = true;
+ manager.supportsTouchEvents = true;
+
+ manager.addEvents();
+
+ expect(element.addEventListener).to.have.been.calledWith('touchstart');
+ expect(element.addEventListener).to.have.been.calledWith('touchcancel');
+ expect(element.addEventListener).to.have.been.calledWith('touchend');
+ expect(element.addEventListener).to.have.been.calledWith('touchmove');
+
+ manager.removeEvents();
+
+ expect(element.removeEventListener).to.have.been.calledWith('touchstart');
+ expect(element.removeEventListener).to.have.been.calledWith('touchcancel');
+ expect(element.removeEventListener).to.have.been.calledWith('touchend');
+ expect(element.removeEventListener).to.have.been.calledWith('touchmove');
+ });
});
describe('onClick', function ()
@@ -1081,7 +1194,7 @@
expect(pointer.renderer.view.style.display).to.equal('none');
});
- it('should not change cursor style if no cursor style provided', function ()
+ it('should not change cursor style if null cursor style provided', function ()
{
const stage = new PIXI.Container();
const graphics = new PIXI.Graphics();
@@ -1101,6 +1214,22 @@
pointer.mousemove(60, 60);
expect(pointer.renderer.view.style.cursor).to.equal('');
});
+
+ it('should use cursor property as css if no style entry', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.cursor = 'text';
+
+ pointer.mousemove(10, 10);
+ expect(pointer.renderer.view.style.cursor).to.equal('text');
+ });
});
describe('recursive hitTesting', function ()
@@ -1298,4 +1427,91 @@
expect(hit).to.equal(behind);
});
});
+
+ describe('InteractionData properties', function ()
+ {
+ it('isPrimary should be set for first touch only', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+
+ pointer.touchstart(10, 10, 1);
+ expect(pointer.interaction.activeInteractionData[1]).to.exist;
+ expect(pointer.interaction.activeInteractionData[1].isPrimary,
+ 'first touch should be primary on touch start').to.be.true;
+ pointer.touchstart(13, 9, 2);
+ expect(pointer.interaction.activeInteractionData[2].isPrimary,
+ 'second touch should not be primary').to.be.false;
+ pointer.touchmove(10, 20, 1);
+ expect(pointer.interaction.activeInteractionData[1].isPrimary,
+ 'first touch should still be primary after move').to.be.true;
+ pointer.touchend(10, 10, 1);
+ pointer.touchmove(13, 29, 2);
+ expect(pointer.interaction.activeInteractionData[2].isPrimary,
+ 'second touch should still not be primary after first is done').to.be.false;
+ });
+ });
+
+ describe('mouse events from pens', function ()
+ {
+ it('should call mousedown handler', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const eventSpy = sinon.spy();
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('mousedown', eventSpy);
+
+ pointer.pendown(10, 10);
+
+ expect(eventSpy).to.have.been.calledOnce;
+ });
+
+ it('should call mousemove handler', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const eventSpy = sinon.spy();
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('mousemove', eventSpy);
+
+ pointer.penmove(10, 10);
+
+ expect(eventSpy).to.have.been.calledOnce;
+ });
+
+ it('should call mouseup handler', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const eventSpy = sinon.spy();
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('mouseup', eventSpy);
+
+ pointer.penup(10, 10);
+
+ expect(eventSpy).to.have.been.calledOnce;
+ });
+ });
});
diff --git a/.travis.yml b/.travis.yml
index 14de334..c4135cb 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,8 +25,7 @@
- Xvfb :99 -screen 0 1024x768x24 -extension RANDR &
script:
- - npm run build
- - xvfb-maybe npm run coverage
+ - xvfb-maybe npm test
- npm run docs
deploy:
diff --git a/README.md b/README.md
index 3b85f85..e1aa27f 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,9 @@
It's easy to get started with Pixi.js! Simply download a [prebuilt build](https://github.com/pixijs/pixi.js/wiki/FAQs#where-can-i-get-a-build)!
-Alternatively, Pixi.js can be installed with [Bower](https://bower.io/#getting-started), [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
+Alternatively, Pixi.js can be installed with [npm](https://docs.npmjs.com/getting-started/what-is-npm) or simply using a content delivery network (CDN) URL to embed Pixi.js directly on your HTML page.
-#### Bower Install
-
-```
-$> bower install pixi.js
-```
+_Note: After v4.5.0, support for the [Bower](https://bower.io) package manager has been dropped. Please see the [release notes](https://github.com/pixijs/pixi.js/releases/tag/v4.5.0) for more information._
#### NPM Install
@@ -62,10 +58,10 @@
#### CDN Install (via cdnjs)
```html
-
+
```
-_Note: `4.4.3` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
+_Note: `4.5.1` can be replaced by any [released](https://github.com/pixijs/pixi.js/releases) version._
### Demos ###
diff --git a/package.json b/package.json
index df623d5..ce17a4f 100644
--- a/package.json
+++ b/package.json
@@ -24,12 +24,13 @@
"watch": "npm run dist -- --watch",
"watch:lib": "npm run lib -- --watch",
"watch:lint": "watch \"eslint scripts src test || exit 0\" src",
- "test": "floss --path test/index.js",
- "test:debug": "npm test -- --debug",
+ "test": "npm run lint && npm run dist && npm run coverage",
+ "unit-test": "floss --path test/index.js",
+ "unit-test:debug": "npm run unit-test -- --debug",
"prerenders": "npm --prefix scripts/renders i scripts/renders",
"renders": "electron scripts/renders",
"precoverage": "rimraf coverage",
- "coverage": "npm test -- -c dist/pixi.js -s -h",
+ "coverage": "npm run unit-test -- -c dist/pixi.js -s -h",
"lint": "eslint scripts src test --max-warnings 0",
"lintfix": "npm run lint --fix",
"prebuild": "npm run lint",
@@ -43,7 +44,7 @@
"publish:patch": "npm version patch && npm publish",
"publish:minor": "npm version minor && npm publish",
"publish:major": "npm version major && npm publish",
- "postversion": "npm run clean && npm run build && npm run lib && npm test",
+ "postversion": "npm run clean && npm run build && npm run lib && npm run unit-test",
"postpublish": "git push && git push --tags && npm run release",
"release": "node scripts/release"
},
@@ -62,6 +63,7 @@
"ismobilejs": "^0.4.0",
"object-assign": "^4.0.1",
"pixi-gl-core": "^1.0.3",
+ "remove-array-items": "^1.0.0",
"resource-loader": "^2.0.6"
},
"devDependencies": {
diff --git a/src/core/graphics/Graphics.js b/src/core/graphics/Graphics.js
index 9def67f..ac5399d 100644
--- a/src/core/graphics/Graphics.js
+++ b/src/core/graphics/Graphics.js
@@ -865,6 +865,19 @@
{
if (data.shape.contains(tempPoint.x, tempPoint.y))
{
+ if (data.holes)
+ {
+ for (let i = 0; i < data.holes.length; i++)
+ {
+ const hole = data.holes[i];
+
+ if (hole.contains(tempPoint.x, tempPoint.y))
+ {
+ return false;
+ }
+ }
+ }
+
return true;
}
}
@@ -994,10 +1007,10 @@
const padding = this.boundsPadding;
this._localBounds.minX = minX - padding;
- this._localBounds.maxX = maxX + (padding * 2);
+ this._localBounds.maxX = maxX + padding;
this._localBounds.minY = minY - padding;
- this._localBounds.maxY = maxY + (padding * 2);
+ this._localBounds.maxY = maxY + padding;
}
/**
diff --git a/src/core/renderers/webgl/WebGLRenderer.js b/src/core/renderers/webgl/WebGLRenderer.js
index d9a1777..dd53b06 100644
--- a/src/core/renderers/webgl/WebGLRenderer.js
+++ b/src/core/renderers/webgl/WebGLRenderer.js
@@ -223,6 +223,9 @@
const maxTextures = gl.getParameter(gl.MAX_TEXTURE_IMAGE_UNITS);
+ this._activeShader = null;
+ this._activeVao = null;
+
this.boundTextures = new Array(maxTextures);
this.emptyTextures = new Array(maxTextures);
@@ -674,8 +677,8 @@
*/
handleContextRestored()
{
- this._initContext();
this.textureManager.removeAll();
+ this._initContext();
}
/**
diff --git a/src/core/sprites/canvas/CanvasSpriteRenderer.js b/src/core/sprites/canvas/CanvasSpriteRenderer.js
index d2ab0c4..15820c0 100644
--- a/src/core/sprites/canvas/CanvasSpriteRenderer.js
+++ b/src/core/sprites/canvas/CanvasSpriteRenderer.js
@@ -126,7 +126,7 @@
if (sprite.tint !== 0xFFFFFF)
{
- if (sprite.cachedTint !== sprite.tint)
+ if (sprite.cachedTint !== sprite.tint || sprite.tintedTexture.tintId !== sprite._texture._updateID)
{
sprite.cachedTint = sprite.tint;
diff --git a/src/core/sprites/canvas/CanvasTinter.js b/src/core/sprites/canvas/CanvasTinter.js
index fd9de5d..af018d4 100644
--- a/src/core/sprites/canvas/CanvasTinter.js
+++ b/src/core/sprites/canvas/CanvasTinter.js
@@ -26,17 +26,28 @@
texture.tintCache = texture.tintCache || {};
- if (texture.tintCache[stringColor])
+ const cachedTexture = texture.tintCache[stringColor];
+
+ let canvas;
+
+ if (cachedTexture)
{
- return texture.tintCache[stringColor];
+ if (cachedTexture.tintId === texture._updateID)
+ {
+ return texture.tintCache[stringColor];
+ }
+
+ canvas = texture.tintCache[stringColor];
+ }
+ else
+ {
+ canvas = CanvasTinter.canvas || document.createElement('canvas');
}
- // clone texture..
- const canvas = CanvasTinter.canvas || document.createElement('canvas');
-
- // CanvasTinter.tintWithPerPixel(texture, stringColor, canvas);
CanvasTinter.tintMethod(texture, color, canvas);
+ canvas.tintId = texture._updateID;
+
if (CanvasTinter.convertTintToImage)
{
// is this better?
diff --git a/src/core/text/Text.js b/src/core/text/Text.js
index a96682f..d81216d 100644
--- a/src/core/text/Text.js
+++ b/src/core/text/Text.js
@@ -134,6 +134,7 @@
this._font = this._style.toFontString();
+ const context = this.context;
const measured = TextMetrics.measureText(this._text, this._style, this._style.wordWrap, this.canvas);
const width = measured.width;
const height = measured.height;
@@ -146,32 +147,32 @@
this.canvas.width = Math.ceil((width + (style.padding * 2)) * this.resolution);
this.canvas.height = Math.ceil((height + (style.padding * 2)) * this.resolution);
- this.context.scale(this.resolution, this.resolution);
+ context.scale(this.resolution, this.resolution);
- this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
+ context.clearRect(0, 0, this.canvas.width, this.canvas.height);
- this.context.font = this._font;
- this.context.strokeStyle = style.stroke;
- this.context.lineWidth = style.strokeThickness;
- this.context.textBaseline = style.textBaseline;
- this.context.lineJoin = style.lineJoin;
- this.context.miterLimit = style.miterLimit;
+ context.font = this._font;
+ context.strokeStyle = style.stroke;
+ context.lineWidth = style.strokeThickness;
+ context.textBaseline = style.textBaseline;
+ context.lineJoin = style.lineJoin;
+ context.miterLimit = style.miterLimit;
let linePositionX;
let linePositionY;
if (style.dropShadow)
{
- this.context.shadowBlur = style.dropShadowBlur;
- this.context.globalAlpha = style.dropShadowAlpha;
+ context.shadowBlur = style.dropShadowBlur;
+ context.globalAlpha = style.dropShadowAlpha;
if (style.dropShadowBlur > 0)
{
- this.context.shadowColor = style.dropShadowColor;
+ context.shadowColor = style.dropShadowColor;
}
else
{
- this.context.fillStyle = style.dropShadowColor;
+ context.fillStyle = style.dropShadowColor;
}
const xShadowOffset = Math.cos(style.dropShadowAngle) * style.dropShadowDistance;
@@ -200,24 +201,24 @@
if (style.stroke && style.strokeThickness)
{
- this.context.strokeStyle = style.dropShadowColor;
+ context.strokeStyle = style.dropShadowColor;
this.drawLetterSpacing(
lines[i],
linePositionX + xShadowOffset + style.padding, linePositionY + yShadowOffset + style.padding,
true
);
- this.context.strokeStyle = style.stroke;
+ context.strokeStyle = style.stroke;
}
}
}
}
// reset the shadow blur and alpha that was set by the drop shadow, for the regular text
- this.context.shadowBlur = 0;
- this.context.globalAlpha = 1;
+ context.shadowBlur = 0;
+ context.globalAlpha = 1;
// set canvas text styles
- this.context.fillStyle = this._generateFillStyle(style, lines);
+ context.fillStyle = this._generateFillStyle(style, lines);
// draw lines line by line
for (let i = 0; i < lines.length; i++)
@@ -314,29 +315,32 @@
*/
updateTexture()
{
+ const canvas = this.canvas;
+
if (this._style.trim)
{
- const trimmed = trimCanvas(this.canvas);
+ const trimmed = trimCanvas(canvas);
- this.canvas.width = trimmed.width;
- this.canvas.height = trimmed.height;
+ canvas.width = trimmed.width;
+ canvas.height = trimmed.height;
this.context.putImageData(trimmed.data, 0, 0);
}
const texture = this._texture;
const style = this._style;
const padding = style.trim ? 0 : style.padding;
+ const baseTexture = texture.baseTexture;
- texture.baseTexture.hasLoaded = true;
- texture.baseTexture.resolution = this.resolution;
+ baseTexture.hasLoaded = true;
+ baseTexture.resolution = this.resolution;
- texture.baseTexture.realWidth = this.canvas.width;
- texture.baseTexture.realHeight = this.canvas.height;
- texture.baseTexture.width = this.canvas.width / this.resolution;
- texture.baseTexture.height = this.canvas.height / this.resolution;
- texture.trim.width = texture._frame.width = this.canvas.width / this.resolution;
- texture.trim.height = texture._frame.height = this.canvas.height / this.resolution;
+ baseTexture.realWidth = canvas.width;
+ baseTexture.realHeight = canvas.height;
+ baseTexture.width = canvas.width / this.resolution;
+ baseTexture.height = canvas.height / this.resolution;
+ texture.trim.width = texture._frame.width = canvas.width / this.resolution;
+ texture.trim.height = texture._frame.height = canvas.height / this.resolution;
texture.trim.x = -padding;
texture.trim.y = -padding;
@@ -346,7 +350,7 @@
// call sprite onTextureUpdate to update scale if _width or _height were set
this._onTextureUpdate();
- texture.baseTexture.emit('update', texture.baseTexture);
+ baseTexture.emit('update', baseTexture);
this.dirty = false;
}
diff --git a/src/core/utils/index.js b/src/core/utils/index.js
index 098b383..22e2d98 100644
--- a/src/core/utils/index.js
+++ b/src/core/utils/index.js
@@ -4,6 +4,7 @@
import pluginTarget from './pluginTarget';
import * as mixins from './mixin';
import * as isMobile from 'ismobilejs';
+import removeItems from 'remove-array-items';
let nextUid = 0;
let saidHello = false;
@@ -35,6 +36,15 @@
* @type {Object}
*/
isMobile,
+
+ /**
+ * @see {@link https://github.com/mreinstein/remove-array-items}
+ *
+ * @memberof PIXI.utils
+ * @function removeItems
+ * @type {Object}
+ */
+ removeItems,
/**
* @see {@link https://github.com/primus/eventemitter3}
*
@@ -333,36 +343,6 @@
}
/**
- * Remove a range of items from an array
- *
- * @memberof PIXI.utils
- * @function removeItems
- * @param {Array<*>} arr The target array
- * @param {number} startIdx The index to begin removing from (inclusive)
- * @param {number} removeCount How many items to remove
- */
-export function removeItems(arr, startIdx, removeCount)
-{
- const length = arr.length;
-
- if (startIdx >= length || removeCount === 0)
- {
- return;
- }
-
- removeCount = (startIdx + removeCount > length ? length - startIdx : removeCount);
-
- const len = length - removeCount;
-
- for (let i = startIdx; i < len; ++i)
- {
- arr[i] = arr[i + removeCount];
- }
-
- arr.length = len;
-}
-
-/**
* @todo Describe property usage
*
* @memberof PIXI.utils
diff --git a/src/extras/AnimatedSprite.js b/src/extras/AnimatedSprite.js
index cf0ff29..a24f07c 100644
--- a/src/extras/AnimatedSprite.js
+++ b/src/extras/AnimatedSprite.js
@@ -354,6 +354,8 @@
this._durations.push(value[i].time);
}
}
+ this.gotoAndStop(0);
+ this.updateTexture();
}
/**
diff --git a/src/extras/BitmapText.js b/src/extras/BitmapText.js
index 0a0e150..1340fcc 100644
--- a/src/extras/BitmapText.js
+++ b/src/extras/BitmapText.js
@@ -1,5 +1,6 @@
import * as core from '../core';
import ObservablePoint from '../core/math/ObservablePoint';
+import settings from '../core/settings';
/**
* A BitmapText object will create a line or multiple lines of text using bitmap font. To
@@ -469,10 +470,11 @@
const data = {};
const info = xml.getElementsByTagName('info')[0];
const common = xml.getElementsByTagName('common')[0];
+ const res = texture.baseTexture.resolution || settings.RESOLUTION;
data.font = info.getAttribute('face');
data.size = parseInt(info.getAttribute('size'), 10);
- data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10);
+ data.lineHeight = parseInt(common.getAttribute('lineHeight'), 10) / res;
data.chars = {};
// parse letters
@@ -484,16 +486,16 @@
const charCode = parseInt(letter.getAttribute('id'), 10);
const textureRect = new core.Rectangle(
- parseInt(letter.getAttribute('x'), 10) + texture.frame.x,
- parseInt(letter.getAttribute('y'), 10) + texture.frame.y,
- parseInt(letter.getAttribute('width'), 10),
- parseInt(letter.getAttribute('height'), 10)
+ (parseInt(letter.getAttribute('x'), 10) / res) + (texture.frame.x / res),
+ (parseInt(letter.getAttribute('y'), 10) / res) + (texture.frame.y / res),
+ parseInt(letter.getAttribute('width'), 10) / res,
+ parseInt(letter.getAttribute('height'), 10) / res
);
data.chars[charCode] = {
- xOffset: parseInt(letter.getAttribute('xoffset'), 10),
- yOffset: parseInt(letter.getAttribute('yoffset'), 10),
- xAdvance: parseInt(letter.getAttribute('xadvance'), 10),
+ xOffset: parseInt(letter.getAttribute('xoffset'), 10) / res,
+ yOffset: parseInt(letter.getAttribute('yoffset'), 10) / res,
+ xAdvance: parseInt(letter.getAttribute('xadvance'), 10) / res,
kerning: {},
texture: new core.Texture(texture.baseTexture, textureRect),
@@ -506,9 +508,9 @@
for (let i = 0; i < kernings.length; i++)
{
const kerning = kernings[i];
- const first = parseInt(kerning.getAttribute('first'), 10);
- const second = parseInt(kerning.getAttribute('second'), 10);
- const amount = parseInt(kerning.getAttribute('amount'), 10);
+ const first = parseInt(kerning.getAttribute('first'), 10) / res;
+ const second = parseInt(kerning.getAttribute('second'), 10) / res;
+ const amount = parseInt(kerning.getAttribute('amount'), 10) / res;
if (data.chars[second])
{
diff --git a/src/extras/index.js b/src/extras/index.js
index a34d7ec..5b98cb1 100644
--- a/src/extras/index.js
+++ b/src/extras/index.js
@@ -3,6 +3,7 @@
* @namespace PIXI.extras
*/
export { default as AnimatedSprite } from './AnimatedSprite';
+export { default as TextureTransform } from './TextureTransform';
export { default as TilingSprite } from './TilingSprite';
export { default as TilingSpriteRenderer } from './webgl/TilingSpriteRenderer';
export { default as BitmapText } from './BitmapText';
diff --git a/src/interaction/InteractionData.js b/src/interaction/InteractionData.js
index f4619a1..84dd1f3 100644
--- a/src/interaction/InteractionData.js
+++ b/src/interaction/InteractionData.js
@@ -43,6 +43,104 @@
* @member {number}
*/
this.identifier = null;
+
+ /**
+ * Indicates whether or not the pointer device that created the event is the primary pointer.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/isPrimary
+ * @type {Boolean}
+ */
+ this.isPrimary = false;
+
+ /**
+ * Indicates which button was pressed on the mouse or pointer device to trigger the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+ * @type {number}
+ */
+ this.button = 0;
+
+ /**
+ * Indicates which buttons are pressed on the mouse or pointer device when the event is triggered.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
+ * @type {number}
+ */
+ this.buttons = 0;
+
+ /**
+ * The width of the pointer's contact along the x-axis, measured in CSS pixels.
+ * radiusX of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/width
+ * @type {number}
+ */
+ this.width = 0;
+
+ /**
+ * The height of the pointer's contact along the y-axis, measured in CSS pixels.
+ * radiusY of TouchEvents will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/height
+ * @type {number}
+ */
+ this.height = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltX
+ * @type {number}
+ */
+ this.tiltX = 0;
+
+ /**
+ * The angle, in degrees, between the pointer device and the screen.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/tiltY
+ * @type {number}
+ */
+ this.tiltY = 0;
+
+ /**
+ * The type of pointer that triggered the event.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerType
+ * @type {string}
+ */
+ this.pointerType = null;
+
+ /**
+ * Pressure applied by the pointing device during the event. A Touch's force property
+ * will be represented by this value.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pressure
+ * @type {number}
+ */
+ this.pressure = 0;
+
+ /**
+ * From TouchEvents (not PointerEvents triggered by touches), the rotationAngle of the Touch.
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Touch/rotationAngle
+ * @type {number}
+ */
+ this.rotationAngle = 0;
+
+ /**
+ * Twist of a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.twist = 0;
+
+ /**
+ * Barrel pressure on a stylus pointer.
+ * @see https://w3c.github.io/pointerevents/#pointerevent-interface
+ * @type {number}
+ */
+ this.tangentialPressure = 0;
+ }
+
+ /**
+ * The unique identifier of the pointer. It will be the same as `identifier`.
+ * @readonly
+ * @member {number}
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/PointerEvent/pointerId
+ */
+ get pointerId()
+ {
+ return this.identifier;
}
/**
@@ -61,4 +159,44 @@
{
return displayObject.worldTransform.applyInverse(globalPos || this.global, point);
}
+
+ /**
+ * Copies properties from normalized event data.
+ *
+ * @param {Touch|MouseEvent|PointerEvent} event The normalized event data
+ * @private
+ */
+ _copyEvent(event)
+ {
+ // isPrimary should only change on touchstart/pointerdown, so we don't want to overwrite
+ // it with "false" on later events when our shim for it on touch events might not be
+ // accurate
+ if (event.isPrimary)
+ {
+ this.isPrimary = true;
+ }
+ this.button = event.button;
+ this.buttons = event.buttons;
+ this.width = event.width;
+ this.height = event.height;
+ this.tiltX = event.tiltX;
+ this.tiltY = event.tiltY;
+ this.pointerType = event.pointerType;
+ this.pressure = event.pressure;
+ this.rotationAngle = event.rotationAngle;
+ this.twist = event.twist || 0;
+ this.tangentialPressure = event.tangentialPressure || 0;
+ }
+
+ /**
+ * Resets the data for pooling.
+ *
+ * @private
+ */
+ _reset()
+ {
+ // isPrimary is the only property that we really need to reset - everything else is
+ // guaranteed to be overwritten
+ this.isPrimary = false;
+ }
}
diff --git a/src/interaction/InteractionManager.js b/src/interaction/InteractionManager.js
index 729469c..8fb9416 100644
--- a/src/interaction/InteractionManager.js
+++ b/src/interaction/InteractionManager.js
@@ -732,7 +732,6 @@
window.addEventListener('pointercancel', this.onPointerCancel, true);
window.addEventListener('pointerup', this.onPointerUp, true);
}
-
else
{
window.document.addEventListener('mousemove', this.onPointerMove, true);
@@ -740,14 +739,17 @@
this.interactionDOMElement.addEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.addEventListener('mouseover', this.onPointerOver, true);
window.addEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
- }
+ // always look directly for touch events so that we can provide original data
+ // In a future version we should change this to being just a fallback and rely solely on
+ // PointerEvents whenever available
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.addEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.addEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.addEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.addEventListener('touchmove', this.onPointerMove, true);
}
this.eventsAdded = true;
@@ -793,14 +795,14 @@
this.interactionDOMElement.removeEventListener('mouseout', this.onPointerOut, true);
this.interactionDOMElement.removeEventListener('mouseover', this.onPointerOver, true);
window.removeEventListener('mouseup', this.onPointerUp, true);
+ }
- if (this.supportsTouchEvents)
- {
- this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
- this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
- this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
- this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
- }
+ if (this.supportsTouchEvents)
+ {
+ this.interactionDOMElement.removeEventListener('touchstart', this.onPointerDown, true);
+ this.interactionDOMElement.removeEventListener('touchcancel', this.onPointerCancel, true);
+ this.interactionDOMElement.removeEventListener('touchend', this.onPointerUp, true);
+ this.interactionDOMElement.removeEventListener('touchmove', this.onPointerMove, true);
}
this.interactionDOMElement = null;
@@ -909,6 +911,12 @@
break;
}
}
+ else if (typeof mode === 'string' && !Object.prototype.hasOwnProperty.call(this.cursorStyles, mode))
+ {
+ // if it mode is a string (not a Symbol) and cursorStyles doesn't have any entry
+ // for the mode, then assume that the dev wants it to be CSS for the cursor.
+ this.interactionDOMElement.style.cursor = mode;
+ }
}
/**
@@ -1116,6 +1124,9 @@
*/
onPointerDown(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
/**
@@ -1150,9 +1161,10 @@
{
this.emit('touchstart', interactionEvent);
}
- else if (event.pointerType === 'mouse')
+ // emit a mouse event for "pen" pointers, the way a browser would emit a fallback event
+ else if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? 'rightdown' : 'mousedown', this.eventData);
}
@@ -1169,8 +1181,7 @@
*/
processPointerDown(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
-
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
if (hit)
@@ -1181,13 +1192,13 @@
}
this.dispatchEvent(displayObject, 'pointerdown', interactionEvent);
- if (e.type === 'touchstart' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchstart', interactionEvent);
}
- else if (e.type === 'mousedown' || e.pointerType === 'mouse')
+ else if (data.pointerType === 'mouse' || data.pointerType === 'pen')
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
if (isRightButton)
{
@@ -1236,9 +1247,9 @@
this.emit(cancelled ? 'pointercancel' : `pointerup${eventAppend}`, interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
- const isRightButton = event.button === 2 || event.which === 3;
+ const isRightButton = event.button === 2;
this.emit(isRightButton ? `rightup${eventAppend}` : `mouseup${eventAppend}`, interactionEvent);
}
@@ -1258,6 +1269,9 @@
*/
onPointerCancel(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, true, this.processPointerCancel);
}
@@ -1270,7 +1284,7 @@
*/
processPointerCancel(interactionEvent, displayObject)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
@@ -1279,7 +1293,7 @@
delete displayObject.trackedPointers[id];
this.dispatchEvent(displayObject, 'pointercancel', interactionEvent);
- if (e.type === 'touchcancel' || e.pointerType === 'touch')
+ if (data.pointerType === 'touch')
{
this.dispatchEvent(displayObject, 'touchcancel', interactionEvent);
}
@@ -1294,6 +1308,9 @@
*/
onPointerUp(event)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && event.pointerType === 'touch') return;
+
this.onPointerComplete(event, false, this.processPointerUp);
}
@@ -1307,20 +1324,20 @@
*/
processPointerUp(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
const trackingData = displayObject.trackedPointers[id];
- const isTouch = (e.type === 'touchend' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type.indexOf('mouse') === 0 || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
// Mouse only
if (isMouse)
{
- const isRightButton = e.button === 2 || e.which === 3;
+ const isRightButton = data.button === 2;
const flags = InteractionTrackingData.FLAGS;
@@ -1393,6 +1410,9 @@
*/
onPointerMove(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
if (events[0].pointerType === 'mouse')
@@ -1424,7 +1444,7 @@
);
this.emit('pointermove', interactionEvent);
if (event.pointerType === 'touch') this.emit('touchmove', interactionEvent);
- if (event.pointerType === 'mouse') this.emit('mousemove', interactionEvent);
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen') this.emit('mousemove', interactionEvent);
}
if (events[0].pointerType === 'mouse')
@@ -1445,11 +1465,11 @@
*/
processPointerMove(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
- const isTouch = (e.type === 'touchmove' || e.pointerType === 'touch');
+ const isTouch = data.pointerType === 'touch';
- const isMouse = (e.type === 'mousemove' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
if (isMouse)
{
@@ -1472,6 +1492,9 @@
*/
onPointerOut(originalEvent)
{
+ // if we support touch events, then only use those for touch events, not pointer events
+ if (this.supportsTouchEvents && originalEvent.pointerType === 'touch') return;
+
const events = this.normalizeToPointerData(originalEvent);
// Only mouse and pointer can call onPointerOut, so events will always be length 1
@@ -1492,7 +1515,7 @@
this.processInteractive(interactionEvent, this.renderer._lastObjectRendered, this.processPointerOverOut, false);
this.emit('pointerout', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseout', interactionEvent);
}
@@ -1514,11 +1537,11 @@
*/
processPointerOverOut(interactionEvent, displayObject, hit)
{
- const e = interactionEvent.data.originalEvent;
+ const data = interactionEvent.data;
const id = interactionEvent.data.identifier;
- const isMouse = (e.type === 'mouseover' || e.type === 'mouseout' || e.pointerType === 'mouse');
+ const isMouse = (data.pointerType === 'mouse' || data.pointerType === 'pen');
let trackingData = displayObject.trackedPointers[id];
@@ -1590,7 +1613,7 @@
}
this.emit('pointerover', interactionEvent);
- if (event.pointerType === 'mouse')
+ if (event.pointerType === 'mouse' || event.pointerType === 'pen')
{
this.emit('mouseover', interactionEvent);
}
@@ -1607,19 +1630,25 @@
{
const pointerId = event.pointerId;
+ let interactionData;
+
if (pointerId === MOUSE_POINTER_ID || event.pointerType === 'mouse')
{
- return this.mouse;
+ interactionData = this.mouse;
}
else if (this.activeInteractionData[pointerId])
{
- return this.activeInteractionData[pointerId];
+ interactionData = this.activeInteractionData[pointerId];
}
-
- const interactionData = this.interactionDataPool.pop() || new InteractionData();
-
- interactionData.identifier = pointerId;
- this.activeInteractionData[pointerId] = interactionData;
+ else
+ {
+ interactionData = this.interactionDataPool.pop() || new InteractionData();
+ interactionData.identifier = pointerId;
+ this.activeInteractionData[pointerId] = interactionData;
+ }
+ // copy properties from the event, so that we can make sure that touch/pointer specific
+ // data is available
+ interactionData._copyEvent(event);
return interactionData;
}
@@ -1637,6 +1666,7 @@
if (interactionData)
{
delete this.activeInteractionData[pointerId];
+ interactionData._reset();
this.interactionDataPool.push(interactionData);
}
}
@@ -1699,7 +1729,10 @@
if (typeof touch.button === 'undefined') touch.button = event.touches.length ? 1 : 0;
if (typeof touch.buttons === 'undefined') touch.buttons = event.touches.length ? 1 : 0;
- if (typeof touch.isPrimary === 'undefined') touch.isPrimary = event.touches.length === 1;
+ if (typeof touch.isPrimary === 'undefined')
+ {
+ touch.isPrimary = event.touches.length === 1 && event.type === 'touchstart';
+ }
if (typeof touch.width === 'undefined') touch.width = touch.radiusX || 1;
if (typeof touch.height === 'undefined') touch.height = touch.radiusY || 1;
if (typeof touch.tiltX === 'undefined') touch.tiltX = 0;
@@ -1707,8 +1740,12 @@
if (typeof touch.pointerType === 'undefined') touch.pointerType = 'touch';
if (typeof touch.pointerId === 'undefined') touch.pointerId = touch.identifier || 0;
if (typeof touch.pressure === 'undefined') touch.pressure = touch.force || 0.5;
- if (typeof touch.rotation === 'undefined') touch.rotation = touch.rotationAngle || 0;
-
+ touch.twist = 0;
+ touch.tangentialPressure = 0;
+ // TODO: Remove these, as layerX/Y is not a standard, is deprecated, has uneven
+ // support, and the fill ins are not quite the same
+ // offsetX/Y might be okay, but is not the same as clientX/Y when the canvas's top
+ // left is not 0,0 on the page
if (typeof touch.layerX === 'undefined') touch.layerX = touch.offsetX = touch.clientX;
if (typeof touch.layerY === 'undefined') touch.layerY = touch.offsetY = touch.clientY;
@@ -1729,7 +1766,8 @@
if (typeof event.pointerType === 'undefined') event.pointerType = 'mouse';
if (typeof event.pointerId === 'undefined') event.pointerId = MOUSE_POINTER_ID;
if (typeof event.pressure === 'undefined') event.pressure = 0.5;
- if (typeof event.rotation === 'undefined') event.rotation = 0;
+ event.twist = 0;
+ event.tangentialPressure = 0;
// mark the mouse event as normalized, just so that we know we did it
event.isNormalized = true;
diff --git a/src/loaders/index.js b/src/loaders/index.js
index daa1fc4..6c3242a 100644
--- a/src/loaders/index.js
+++ b/src/loaders/index.js
@@ -69,12 +69,12 @@
// Override the destroy function
// making sure to destroy the current Loader
AppPrototype._parentDestroy = AppPrototype.destroy;
-AppPrototype.destroy = function destroy()
+AppPrototype.destroy = function destroy(removeView)
{
if (this._loader)
{
this._loader.destroy();
this._loader = null;
}
- this._parentDestroy();
+ this._parentDestroy(removeView);
};
diff --git a/src/particles/ParticleContainer.js b/src/particles/ParticleContainer.js
index 63f7791..6d98098 100644
--- a/src/particles/ParticleContainer.js
+++ b/src/particles/ParticleContainer.js
@@ -1,4 +1,5 @@
import * as core from '../core';
+import { hex2rgb } from '../core/utils';
/**
* The ParticleContainer class is a really fast version of the Container built solely for speed,
@@ -121,6 +122,18 @@
this.baseTexture = null;
this.setProperties(properties);
+
+ /**
+ * The tint applied to the container.
+ * This is a hex value. A value of 0xFFFFFF will remove any tint effect.
+ *
+ * @private
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ this._tint = null;
+ this._tintRGB = [];
+ this.tint = 0xFFFFFF;
}
/**
@@ -153,6 +166,24 @@
}
/**
+ * The tint applied to the container. This is a hex value.
+ * A value of 0xFFFFFF will remove any tint effect.
+ ** IMPORTANT: This is a webGL only feature and will be ignored by the canvas renderer.
+ * @member {number}
+ * @default 0xFFFFFF
+ */
+ get tint()
+ {
+ return this._tint;
+ }
+
+ set tint(value) // eslint-disable-line require-jsdoc
+ {
+ this._tint = value;
+ hex2rgb(value, this._tintRGB);
+ }
+
+ /**
* Renders the container using the WebGL renderer
*
* @private
diff --git a/src/particles/webgl/ParticleRenderer.js b/src/particles/webgl/ParticleRenderer.js
index 1b4323b..16e0c9c 100644
--- a/src/particles/webgl/ParticleRenderer.js
+++ b/src/particles/webgl/ParticleRenderer.js
@@ -153,6 +153,7 @@
this.shader.uniforms.projectionMatrix = m.toArray(true);
this.shader.uniforms.uAlpha = container.worldAlpha;
+ this.shader.uniforms.tint = container._tintRGB;
// make sure the texture is bound..
const baseTexture = children[0]._texture.baseTexture;
diff --git a/src/particles/webgl/ParticleShader.js b/src/particles/webgl/ParticleShader.js
index 8c8b056..ebd80d9 100644
--- a/src/particles/webgl/ParticleShader.js
+++ b/src/particles/webgl/ParticleShader.js
@@ -49,9 +49,10 @@
'uniform sampler2D uSampler;',
'uniform float uAlpha;',
+ 'uniform vec3 tint;',
'void main(void){',
- ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * uAlpha;',
+ ' vec4 color = texture2D(uSampler, vTextureCoord) * vColor * vec4(tint * uAlpha, uAlpha);',
' if (color.a == 0.0) discard;',
' gl_FragColor = color;',
'}',
diff --git a/src/prepare/BasePrepare.js b/src/prepare/BasePrepare.js
index e45c4dc..3ca0774 100644
--- a/src/prepare/BasePrepare.js
+++ b/src/prepare/BasePrepare.js
@@ -434,12 +434,9 @@
{
if (item instanceof core.TextStyle)
{
- const font = core.Text.getFontStyle(item);
+ const font = item.toFontString();
- if (!core.Text.fontPropertiesCache[font])
- {
- core.Text.calculateFontProperties(font);
- }
+ core.TextMetrics.measureFont(font);
return true;
}
diff --git a/test/core/Application.js b/test/core/Application.js
index aa726b0..00eb286 100644
--- a/test/core/Application.js
+++ b/test/core/Application.js
@@ -17,4 +17,21 @@
done();
});
});
+
+ it('should remove canvas when destroyed', function (done)
+ {
+ const app = new PIXI.Application();
+ const view = app.view;
+
+ expect(view).to.be.instanceof(HTMLCanvasElement);
+ document.body.appendChild(view);
+
+ app.ticker.addOnce(() =>
+ {
+ expect(document.body.contains(view)).to.be.true;
+ app.destroy(true);
+ expect(document.body.contains(view)).to.be.false;
+ done();
+ });
+ });
});
diff --git a/test/core/Graphics.js b/test/core/Graphics.js
index 11e47a0..505df4c 100644
--- a/test/core/Graphics.js
+++ b/test/core/Graphics.js
@@ -140,6 +140,28 @@
expect(graphics.containsPoint(point)).to.be.false;
});
+
+ it('should return false with hole', function ()
+ {
+ const point1 = new PIXI.Point(1, 1);
+ const point2 = new PIXI.Point(5, 5);
+ const graphics = new PIXI.Graphics();
+
+ graphics.beginFill(0)
+ .moveTo(0, 0)
+ .lineTo(10, 0)
+ .lineTo(10, 10)
+ .lineTo(0, 10)
+ // draw hole
+ .moveTo(2, 2)
+ .lineTo(8, 2)
+ .lineTo(8, 8)
+ .lineTo(2, 8)
+ .addHole();
+
+ expect(graphics.containsPoint(point1)).to.be.true;
+ expect(graphics.containsPoint(point2)).to.be.false;
+ });
});
describe('arc', function ()
diff --git a/test/core/Texture.js b/test/core/Texture.js
index 6e1109c..0a7919d 100644
--- a/test/core/Texture.js
+++ b/test/core/Texture.js
@@ -70,26 +70,6 @@
expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
});
- it('should be added to the texture cache correctly using legacy addTextureToCache, '
- + 'and should remove also remove the base texture from its cache with removeTextureFromCache', function ()
- {
- cleanCache();
-
- const texture = new PIXI.Texture(new PIXI.BaseTexture());
-
- PIXI.BaseTexture.addToCache(texture.baseTexture, NAME);
- PIXI.Texture.addTextureToCache(texture, NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(0);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(texture.baseTexture);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(texture);
- PIXI.Texture.removeTextureFromCache(NAME);
- expect(texture.baseTexture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(texture.textureCacheIds.indexOf(NAME)).to.equal(-1);
- expect(PIXI.utils.BaseTextureCache[NAME]).to.equal(undefined);
- expect(PIXI.utils.TextureCache[NAME]).to.equal(undefined);
- });
-
it('should remove Texture from entire cache using removeFromCache (by Texture instance)', function ()
{
cleanCache();
diff --git a/test/core/util.js b/test/core/util.js
index 239aaeb..68c7e6d 100755
--- a/test/core/util.js
+++ b/test/core/util.js
@@ -280,39 +280,9 @@
describe('.removeItems', function ()
{
- var arr;
-
- beforeEach(() =>
+ it('should exist', function ()
{
- arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
- });
-
- it('should return if the start index is greater than or equal to the length of the array', function ()
- {
- PIXI.utils.removeItems(arr, arr.length + 1, 5);
- expect(arr.length).to.be.equal(10);
- });
-
- it('should return if the remove count is 0', function ()
- {
- PIXI.utils.removeItems(arr, 2, 0);
- expect(arr.length).to.be.equal(10);
- });
-
- it('should remove the number of elements specified from the array, starting from the start index', function ()
- {
- const res = [1, 2, 3, 8, 9, 10];
-
- PIXI.utils.removeItems(arr, 3, 4);
- expect(arr).to.be.deep.equal(res);
- });
-
- it('should remove other elements if delete count is > than the number of elements after start index', function ()
- {
- const res = [1, 2, 3, 4, 5, 6, 7];
-
- PIXI.utils.removeItems(arr, 7, 10);
- expect(arr).to.be.deep.equal(res);
+ expect(PIXI.utils.removeItems).to.be.a('function');
});
});
diff --git a/test/interaction/InteractionManager.js b/test/interaction/InteractionManager.js
index de81597..3f84d6a 100644
--- a/test/interaction/InteractionManager.js
+++ b/test/interaction/InteractionManager.js
@@ -128,6 +128,95 @@
});
});
+ describe('touch vs pointer', function ()
+ {
+ it('should call touchstart and pointerdown when touch event and pointer supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10);
+
+ expect(touchSpy).to.have.been.calledOnce;
+ expect(pointerSpy).to.have.been.calledOnce;
+ });
+
+ it('should not call touchstart or pointerdown when pointer event and touch supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10, 0, true);
+
+ expect(touchSpy).to.not.have.been.called;
+ expect(pointerSpy).to.not.have.been.called;
+ });
+
+ it('should call touchstart and pointerdown when touch event and pointer not supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, false);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10);
+
+ expect(touchSpy).to.have.been.calledOnce;
+ expect(pointerSpy).to.have.been.calledOnce;
+ });
+
+ it('should call touchstart and pointerdown when pointer event and touch not supported', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const touchSpy = sinon.spy(function touchListen() { /* noop */ });
+ const pointerSpy = sinon.spy(function pointerListen() { /* noop */ });
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ pointer.interaction.supportsTouchEvents = false;
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('touchstart', touchSpy);
+ graphics.on('pointerdown', pointerSpy);
+
+ pointer.touchstart(10, 10, 0, true);
+
+ expect(touchSpy).to.have.been.calledOnce;
+ expect(pointerSpy).to.have.been.calledOnce;
+ });
+ });
+
describe('add/remove events', function ()
{
let stub;
@@ -283,7 +372,7 @@
expect(element.removeEventListener).to.have.been.calledWith('mouseover');
});
- it('should add and remove touch events to element', function ()
+ it('should add and remove touch events to element without pointer events', function ()
{
const manager = new PIXI.interaction.InteractionManager(sinon.stub());
const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() };
@@ -306,6 +395,30 @@
expect(element.removeEventListener).to.have.been.calledWith('touchend');
expect(element.removeEventListener).to.have.been.calledWith('touchmove');
});
+
+ it('should add and remove touch events to element with pointer events', function ()
+ {
+ const manager = new PIXI.interaction.InteractionManager(sinon.stub());
+ const element = { style: {}, addEventListener: sinon.stub(), removeEventListener: sinon.stub() };
+
+ manager.interactionDOMElement = element;
+ manager.supportsPointerEvents = true;
+ manager.supportsTouchEvents = true;
+
+ manager.addEvents();
+
+ expect(element.addEventListener).to.have.been.calledWith('touchstart');
+ expect(element.addEventListener).to.have.been.calledWith('touchcancel');
+ expect(element.addEventListener).to.have.been.calledWith('touchend');
+ expect(element.addEventListener).to.have.been.calledWith('touchmove');
+
+ manager.removeEvents();
+
+ expect(element.removeEventListener).to.have.been.calledWith('touchstart');
+ expect(element.removeEventListener).to.have.been.calledWith('touchcancel');
+ expect(element.removeEventListener).to.have.been.calledWith('touchend');
+ expect(element.removeEventListener).to.have.been.calledWith('touchmove');
+ });
});
describe('onClick', function ()
@@ -1081,7 +1194,7 @@
expect(pointer.renderer.view.style.display).to.equal('none');
});
- it('should not change cursor style if no cursor style provided', function ()
+ it('should not change cursor style if null cursor style provided', function ()
{
const stage = new PIXI.Container();
const graphics = new PIXI.Graphics();
@@ -1101,6 +1214,22 @@
pointer.mousemove(60, 60);
expect(pointer.renderer.view.style.cursor).to.equal('');
});
+
+ it('should use cursor property as css if no style entry', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.cursor = 'text';
+
+ pointer.mousemove(10, 10);
+ expect(pointer.renderer.view.style.cursor).to.equal('text');
+ });
});
describe('recursive hitTesting', function ()
@@ -1298,4 +1427,91 @@
expect(hit).to.equal(behind);
});
});
+
+ describe('InteractionData properties', function ()
+ {
+ it('isPrimary should be set for first touch only', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const pointer = this.pointer = new MockPointer(stage);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+
+ pointer.touchstart(10, 10, 1);
+ expect(pointer.interaction.activeInteractionData[1]).to.exist;
+ expect(pointer.interaction.activeInteractionData[1].isPrimary,
+ 'first touch should be primary on touch start').to.be.true;
+ pointer.touchstart(13, 9, 2);
+ expect(pointer.interaction.activeInteractionData[2].isPrimary,
+ 'second touch should not be primary').to.be.false;
+ pointer.touchmove(10, 20, 1);
+ expect(pointer.interaction.activeInteractionData[1].isPrimary,
+ 'first touch should still be primary after move').to.be.true;
+ pointer.touchend(10, 10, 1);
+ pointer.touchmove(13, 29, 2);
+ expect(pointer.interaction.activeInteractionData[2].isPrimary,
+ 'second touch should still not be primary after first is done').to.be.false;
+ });
+ });
+
+ describe('mouse events from pens', function ()
+ {
+ it('should call mousedown handler', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const eventSpy = sinon.spy();
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('mousedown', eventSpy);
+
+ pointer.pendown(10, 10);
+
+ expect(eventSpy).to.have.been.calledOnce;
+ });
+
+ it('should call mousemove handler', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const eventSpy = sinon.spy();
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('mousemove', eventSpy);
+
+ pointer.penmove(10, 10);
+
+ expect(eventSpy).to.have.been.calledOnce;
+ });
+
+ it('should call mouseup handler', function ()
+ {
+ const stage = new PIXI.Container();
+ const graphics = new PIXI.Graphics();
+ const eventSpy = sinon.spy();
+ const pointer = this.pointer = new MockPointer(stage, null, null, true);
+
+ stage.addChild(graphics);
+ graphics.beginFill(0xFFFFFF);
+ graphics.drawRect(0, 0, 50, 50);
+ graphics.interactive = true;
+ graphics.on('mouseup', eventSpy);
+
+ pointer.penup(10, 10);
+
+ expect(eventSpy).to.have.been.calledOnce;
+ });
+ });
});
diff --git a/test/interaction/MockPointer.js b/test/interaction/MockPointer.js
index 7403d90..80eb76a 100644
--- a/test/interaction/MockPointer.js
+++ b/test/interaction/MockPointer.js
@@ -30,6 +30,7 @@
this.createdPointerEvent = true;
}
+ this.activeTouches = [];
this.stage = stage;
this.renderer = new PIXI.CanvasRenderer(width || 100, height || 100);
this.renderer.sayHello = () => { /* empty */ };
@@ -73,35 +74,110 @@
}
/**
+ * [createEvent description]
+ * @param {string} eventType `type` of event
+ * @param {number} x pointer x position
+ * @param {number} y pointer y position
+ * @param {number} [identifier] pointer id for touch events
+ * @param {boolean} [asPointer] If it should be a PointerEvent from a mouse or touch
+ * @param {boolean} [onCanvas=true] If the event should be on the canvas (as opposed to a different element)
+ * @return {Event} Generated MouseEvent, TouchEvent, or PointerEvent
+ */
+ createEvent(eventType, x, y, identifier, asPointer, onCanvas = true)
+ {
+ let event;
+
+ if (eventType.startsWith('mouse'))
+ {
+ if (asPointer)
+ {
+ event = new PointerEvent(eventType.replace('mouse', 'pointer'), {
+ pointerType: 'mouse',
+ clientX: x,
+ clientY: y,
+ preventDefault: sinon.stub(),
+ });
+ }
+ else
+ {
+ event = new MouseEvent(eventType, {
+ clientX: x,
+ clientY: y,
+ preventDefault: sinon.stub(),
+ });
+ }
+ if (onCanvas)
+ {
+ Object.defineProperty(event, 'target', { value: this.renderer.view });
+ }
+ }
+ else if (eventType.startsWith('touch'))
+ {
+ if (asPointer)
+ {
+ eventType = eventType.replace('touch', 'pointer').replace('start', 'down').replace('end', 'up');
+ event = new PointerEvent(eventType, {
+ pointerType: 'touch',
+ pointerId: identifier || 0,
+ clientX: x,
+ clientY: y,
+ preventDefault: sinon.stub(),
+ });
+ Object.defineProperty(event, 'target', { value: this.renderer.view });
+ }
+ else
+ {
+ const touch = new Touch({ identifier: identifier || 0, target: this.renderer.view });
+
+ if (eventType.endsWith('start'))
+ {
+ this.activeTouches.push(touch);
+ }
+ else if (eventType.endsWith('end') || eventType.endsWith('leave'))
+ {
+ for (let i = 0; i < this.activeTouches.length; ++i)
+ {
+ if (this.activeTouches[i].identifier === touch.identifier)
+ {
+ this.activeTouches.splice(i, 1);
+ break;
+ }
+ }
+ }
+ event = new TouchEvent(eventType, {
+ preventDefault: sinon.stub(),
+ changedTouches: [touch],
+ touches: this.activeTouches,
+ });
+
+ Object.defineProperty(event, 'target', { value: this.renderer.view });
+ }
+ }
+ else
+ {
+ event = new PointerEvent(eventType, {
+ pointerType: 'pen',
+ pointerId: identifier || 0,
+ clientX: x,
+ clientY: y,
+ preventDefault: sinon.stub(),
+ });
+ Object.defineProperty(event, 'target', { value: this.renderer.view });
+ }
+
+ this.setPosition(x, y);
+ this.render();
+
+ return event;
+ }
+
+ /**
* @param {number} x - pointer x position
* @param {number} y - pointer y position
* @param {boolean} [asPointer] - if it should be a PointerEvent from a mouse
*/
mousemove(x, y, asPointer)
{
- let mouseEvent;
-
- if (asPointer)
- {
- mouseEvent = new PointerEvent('pointermove', {
- pointerType: 'mouse',
- clientX: x,
- clientY: y,
- preventDefault: sinon.stub(),
- });
- }
- else
- {
- mouseEvent = new MouseEvent('mousemove', {
- clientX: x,
- clientY: y,
- preventDefault: sinon.stub(),
- });
- }
-
- Object.defineProperty(mouseEvent, 'target', { value: this.renderer.view });
- this.setPosition(x, y);
- this.render();
// mouseOverRenderer state should be correct, so mouse position to view rect
const rect = new PIXI.Rectangle(0, 0, this.renderer.width, this.renderer.height);
@@ -109,74 +185,102 @@
{
if (!this.interaction.mouseOverRenderer)
{
- this.interaction.onPointerOver(new MouseEvent('mouseover', {
- clientX: x,
- clientY: y,
- preventDefault: sinon.stub(),
- }));
+ this.interaction.onPointerOver(this.createEvent('mouseover', x, y, null, asPointer));
}
- this.interaction.onPointerMove(mouseEvent);
+ this.interaction.onPointerMove(this.createEvent('mousemove', x, y, null, asPointer));
}
else
{
- this.interaction.onPointerOut(new MouseEvent('mouseout', {
- clientX: x,
- clientY: y,
- preventDefault: sinon.stub(),
- }));
+ this.interaction.onPointerOut(this.createEvent('mouseout', x, y, null, asPointer));
}
}
/**
* @param {number} x - pointer x position
* @param {number} y - pointer y position
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a mouse
*/
- click(x, y)
+ click(x, y, asPointer)
{
- this.mousedown(x, y);
- this.mouseup(x, y);
+ this.mousedown(x, y, asPointer);
+ this.mouseup(x, y, asPointer);
}
/**
* @param {number} x - pointer x position
* @param {number} y - pointer y position
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a mouse
*/
- mousedown(x, y)
+ mousedown(x, y, asPointer)
{
- const mouseEvent = new MouseEvent('mousedown', {
- clientX: x,
- clientY: y,
- preventDefault: sinon.stub(),
- });
-
- Object.defineProperty(mouseEvent, 'target', { value: this.renderer.view });
-
- this.setPosition(x, y);
- this.render();
- this.interaction.onPointerDown(mouseEvent);
+ this.interaction.onPointerDown(this.createEvent('mousedown', x, y, null, asPointer));
}
/**
* @param {number} x - pointer x position
* @param {number} y - pointer y position
* @param {boolean} [onCanvas=true] - if the event happend on the Canvas element or not
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a mouse
*/
- mouseup(x, y, onCanvas = true)
+ mouseup(x, y, onCanvas = true, asPointer = false)
{
- const mouseEvent = new MouseEvent('mouseup', {
- clientX: x,
- clientY: y,
- preventDefault: sinon.stub(),
- });
+ this.interaction.onPointerUp(this.createEvent('mouseup', x, y, null, asPointer, onCanvas));
+ }
- if (onCanvas)
- {
- Object.defineProperty(mouseEvent, 'target', { value: this.renderer.view });
- }
+ /**
+ * @param {number} x - pointer x position
+ * @param {number} y - pointer y position
+ * @param {number} [identifier] - pointer id
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a touch
+ */
+ tap(x, y, identifier, asPointer)
+ {
+ this.touchstart(x, y, identifier, asPointer);
+ this.touchend(x, y, identifier, asPointer);
+ }
- this.setPosition(x, y);
- this.render();
- this.interaction.onPointerUp(mouseEvent);
+ /**
+ * @param {number} x - pointer x position
+ * @param {number} y - pointer y position
+ * @param {number} [identifier] - pointer id
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a touch
+ */
+ touchstart(x, y, identifier, asPointer)
+ {
+ this.interaction.onPointerDown(this.createEvent('touchstart', x, y, identifier, asPointer));
+ }
+
+ /**
+ * @param {number} x - pointer x position
+ * @param {number} y - pointer y position
+ * @param {number} [identifier] - pointer id
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a touch
+ */
+ touchmove(x, y, identifier, asPointer)
+ {
+ this.interaction.onPointerMove(this.createEvent('touchmove', x, y, identifier, asPointer));
+ }
+
+ /**
+ * @param {number} x - pointer x position
+ * @param {number} y - pointer y position
+ * @param {number} [identifier] - pointer id
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a touch
+ */
+ touchend(x, y, identifier, asPointer)
+ {
+ this.interaction.onPointerUp(this.createEvent('touchend', x, y, identifier, asPointer));
+ }
+
+ /**
+ * @param {number} x - pointer x position
+ * @param {number} y - pointer y position
+ * @param {number} [identifier] - pointer id
+ * @param {boolean} [asPointer] - if it should be a PointerEvent from a touch
+ */
+ touchleave(x, y, identifier, asPointer)
+ {
+ this.interaction.onPointerOut(this.createEvent('touchleave', x, y, identifier, asPointer));
}
/**
@@ -184,10 +288,9 @@
* @param {number} y - pointer y position
* @param {number} [identifier] - pointer id
*/
- tap(x, y, identifier)
+ pendown(x, y, identifier)
{
- this.touchstart(x, y, identifier);
- this.touchend(x, y, identifier);
+ this.interaction.onPointerDown(this.createEvent('pointerdown', x, y, identifier, true));
}
/**
@@ -195,20 +298,9 @@
* @param {number} y - pointer y position
* @param {number} [identifier] - pointer id
*/
- touchstart(x, y, identifier)
+ penmove(x, y, identifier)
{
- const touchEvent = new TouchEvent('touchstart', {
- preventDefault: sinon.stub(),
- changedTouches: [
- new Touch({ identifier: identifier || 0, target: this.renderer.view }),
- ],
- });
-
- Object.defineProperty(touchEvent, 'target', { value: this.renderer.view });
-
- this.setPosition(x, y);
- this.render();
- this.interaction.onPointerDown(touchEvent);
+ this.interaction.onPointerMove(this.createEvent('pointermove', x, y, identifier, true));
}
/**
@@ -216,41 +308,9 @@
* @param {number} y - pointer y position
* @param {number} [identifier] - pointer id
*/
- touchend(x, y, identifier)
+ penup(x, y, identifier)
{
- const touchEvent = new TouchEvent('touchend', {
- preventDefault: sinon.stub(),
- changedTouches: [
- new Touch({ identifier: identifier || 0, target: this.renderer.view }),
- ],
- });
-
- Object.defineProperty(touchEvent, 'target', { value: this.renderer.view });
-
- this.setPosition(x, y);
- this.render();
- this.interaction.onPointerUp(touchEvent);
- }
-
- /**
- * @param {number} x - pointer x position
- * @param {number} y - pointer y position
- * @param {number} [identifier] - pointer id
- */
- touchleave(x, y, identifier)
- {
- const touchEvent = new TouchEvent('touchleave', {
- preventDefault: sinon.stub(),
- changedTouches: [
- new Touch({ identifier: identifier || 0, target: this.renderer.view }),
- ],
- });
-
- Object.defineProperty(touchEvent, 'target', { value: this.renderer.view });
-
- this.setPosition(x, y);
- this.render();
- this.interaction.onPointerOut(touchEvent);
+ this.interaction.onPointerUp(this.createEvent('pointerup', x, y, identifier, true));
}
}